/* Nicholas Lineback and Jeremy Cowan
   COSC 530 Computer Architecture
   Project 1
   Cache Simulator
   2011-10-20
*/

#include "init.h"

// command line defaults
unsigned int gWordSize  = 32;
unsigned int gBlockSize = 8;
unsigned int gNumBlocks = 32;
unsigned int gAssociativity = 1;
unsigned int gHitTime = 1;
unsigned int gL2Time = 5;
unsigned int gL3Time = 20;
unsigned int gMainMemTime = 100;
unsigned int gL2Size = 0;
unsigned int gL3Size = 0;
char* gFileName = NULL; 
bool gVerbose = false;
//===========================================================
// usage()
// OVERVIEW: prints usage
//===========================================================
void usage()
{
  std::cout << "cacheSimulator [options] file" << std::endl;
  std::cout << "   option                             default\n";
  std::cout << "   --wordSize      size of a word     32\n";
  std::cout << "   --blockSize     words in a block   8\n";
  std::cout << "   --numBlocks     blocks in L1       32\n";
  std::cout << "   --associativity                    1\n";
  std::cout << "   --hitTime       L1 hit time        1\n";
  std::cout << "   --memTime       time to main       100\n";
  std::cout << "   --L2            blocks in L2       0\n";
  std::cout << "   --L3            blocks in L3       0\n";
  std::cout << "   --L2Time        L2 hit time        5\n";
  std::cout << "   --L3Time        L3 hit time        20\n";
  std::cout << "   --verbose       print all info     false\n";
  std::cout << "   --help          print this message\n";
}

//===========================================================
// parseArgs(argc, argv)
// OVERVIEW: parses command line arguments
// MODIFIES: gWordSize, gBlockSize, gNumBlocks, gAssociativity,
//           gHitTime, gL2Time, gL3Time, gMainMemTime, gL2Size,
//           gL3Size, and gFileName
//===========================================================
void parseArgs( int argc, char** argv )
{
  int c;
  int option_index = 0;

  while(1)
  {
    static struct option long_options[]=
      {
	{"wordSize",                  required_argument, 0, 'w'},
	{"blockSize",                 required_argument, 0, 'b'},
	{"numBlocks",                 required_argument, 0, 'n'},
	{"associativity",             required_argument, 0, 'a'},
	{"help",                      no_argument      , 0, 'h'},
	{"hitTime",                   required_argument, 0, 'H'},
	{"memTime",                   required_argument, 0, 'm'},
	{"L2",                        required_argument, 0, 'l'},
	{"L3",                        required_argument, 0, 'k'},
	{"L2Time",                    required_argument, 0, 't'},
	{"L3Time",                    required_argument, 0, 'T'},
	{"verbose",                   no_argument,       0, 'v'},
	{0,0,0,0}
      };
    
    c = getopt_long(argc, 
                    argv, 
                    "w:b:n:a:hH:m:l:k:t:T:v", 
                    long_options, 
                    &option_index);
    
    if(c == -1)
    {
      break;
    }
    
    switch(c)
    {
    case 'w':
      if( sscanf(optarg, "%d", &gWordSize) != 1)
      {
	fprintf(stderr,
		"Warning: ignoring value for command line option"
		" wordSize\n");
      }
      break;
    case 'b':
      if( sscanf(optarg, "%d", &gBlockSize) != 1)
      {
	fprintf(stderr,
		"Warning: ignoring value for command line option"
		" blockSize\n");
      }
      break;
    case 'n':
      if( sscanf(optarg, "%d", &gNumBlocks) != 1)
      {
	fprintf(stderr,
		"Warning: ignoring value for command line option"
		" numBlocks\n");
      }
      break;
    case 'a':
      if( sscanf(optarg, "%d", &gAssociativity) != 1)
      {
	fprintf(stderr,
		"Warning: ignoring value for command line option"
		" associativity\n");
      }
      break;
    case 'l':
      if( sscanf(optarg, "%d", &gL2Size) != 1)
      {
	fprintf(stderr,
		"Warning: ignoring value for command line option"
		" L2\n");
      }
      break;
    case 'k':
      if( sscanf(optarg, "%d", &gL3Size) != 1)
      {
	fprintf(stderr,
		"Warning: ignoring value for command line option"
		" L3\n");
      }
      break;

    case 'h':
      usage();
      break;
    case 'H':
      if( sscanf(optarg, "%d", &gHitTime) != 1)
      {
	fprintf(stderr,
		"Warning: ignoring value for command line option"
		" hitTime\n");
      }
      break;
    case 'm':
      if( sscanf(optarg, "%d", &gMainMemTime) != 1)
      {
	fprintf(stderr,
		"Warning: ignoring value for command line option"
		" memTime\n");
      }
      break;
    case 't':
      if( sscanf(optarg, "%d", &gL2Time) != 1)
      {
	fprintf(stderr,
		"Warning: ignoring value for command line option"
		" L3Time\n");
      }
      break;
    case 'T':
      if( sscanf(optarg, "%d", &gL3Time) != 1)
      {
	fprintf(stderr,
		"Warning: ignoring value for command line option"
		" L3Time\n");
      }
      break;
    case 'v':
      gVerbose = true;
      break;
    case '?':
      usage();
      break;
    default:
      abort();
    }
  }
  gFileName = argv[optind];
}

//==========================================================
// parseFile(&inst_list)
// OVERVIEW: parses file of mem instructions
// MODIFIES: inst_list
// THROWS: fileParseException
//==========================================================
void parseFile(std::vector<mem_instruction>& inst_list)
{
  std::string line;
  std::ifstream file;
  file.exceptions ( std::ifstream::failbit ); 
  try
  {
    file.open(gFileName);
  }
  catch(std::ios_base::failure e)
  {
    std::cout << gFileName << " is not a valid file name." << std::endl;
    throw fileParseException("invalid file name");
    return;
  }
  try
  {
    while(getline(file,line))
    {
      char temp;
      std::istringstream sstream(line);
      sstream.exceptions(std::ios::failbit);
      mem_instruction newInstruction;
      try
      {
	sstream >> std::hex >> newInstruction.address;
	sstream >> temp;
      }
      catch(std::ios_base::failure e)
      {
	std::cout << "ERROR: invalid file format" << std::endl;
	throw fileParseException("invalid file format");
	return;
      }
      if(toupper(temp) == 'W')
	newInstruction.read_write = 1;
      else if(toupper(temp) == 'R')
	newInstruction.read_write = 0;
      else
      {
	std::cout << "WARNING: invalid read/write flag assuming read"
		  << std::endl;
	newInstruction.read_write = 0;
      }
      inst_list.push_back(newInstruction);
    }
  }
  catch(std::ios_base::failure e)
  {
    return;
  }
}
